6.11. Конструкция из классов
Конструкция из классов
Буквально - нужно по папочкам всё разложить - в этом структурировании и вся суть грамотности
Так, мы рассмотрели различные типы классов. Как их правильно проектировать? Разумеется, пропускаем многие этапы, и фокусируемся на коде.
Определив нужные классы, мы выбираем наиболее точное имя и комбинируем его, к примеру, если есть Service, до можно сделать UserService. К примеру, UserEmailNotifier, UserModel, UserHelper и так далее. Затем можно свериться с паттернами, и только потом работать.
Какие же классы нам определять?
- Определяем класс - точку входа, к примеру, это Main, Application, Bootstrapper, Program.
- Определяем классы-интерфейсы (абстракцию):
- описывающие поведение (контракты) - Interface, Contract, Service, Repository, DAO;
- создающие объекты - Factory, Builder;
- реализующие алгоритмы - Strategy;
- обрабатывающие события - Listener, Observer, EventListener;
- выполняющие операции как объект - Command;
- проверяющие бизнес-правила - Specification, Validator, Rule;
- передающие данные между слоями - Mapper, Transformer, Converter, DTO.
- Определяем классы, которые реализуют сервис или логику:
- бизнес-логика - ServiceImpl, Service, UseCase, DomainService;
- работа с данными - RepositoryImpl, DAOImpl, UnitOfWork;
- валидация данных - Validator, RuleEngine, SpecificationValidator;
- авторизация/аутентификация - AuthService, Authenticator, Authorizer, TokenManager;
- логирование - Logger, AuditLogger;
- обработка ошибок - ExceptionHandler, ErrorHandler, Notifier.
- Определяем классы, ответственные за обработку событий и сообщений:
- слушающие события - EventListener, MessageConsumer;
- отправители - EventDispatcher, MessageProducer;
- обработчики - RequestHandler, TaskRunner, Processor.
- Определяем классы, которые работают с внешними системами:
- внешний клиент - HttpClient, Client, Integrator, ExternalService;
- отправка уведомлений - Mailer, SmsSender, Notifier;
- управление кэшем - CacheManager, Cacher, CachedService.
- Определяем классы-модели предметной области:
- сущности с ID - Entity, User, Order, Customer;
- объекты без ID - ValueObject, VO, Address, Money;
- корень агрегата - AggregateRoot, OrderAggregate;
- представление UI - Model, ViewModel, Presenter, View.
- Определяем классы для работы с данными:
- парсеры - Parser, JsonParser, XmlParser;
- форматтеры - Formatter, DateFormatter, CurrencyFormatter;
- преобразование - Mapper, Transformer, Converter;
- загрузка/сохранение - Loader, Saver, Importer, Exporter;
- агрегация данных - Aggregator, ReportGenerator, Analyzer.
- Определяем классы для работы с инфраструктурой:
- управление с соединениями - ConnectionManager, Pool, DataSource;
- миграции - Migrator, SchemaManager;
- кэширование - CacheManager, CachedService;
- шедулеры (планировщики) - Scheduler, TaskScheduler, JobRunner.
- Определяем классы для тестирования:
- юнит-тестирование - Spec, Test, UnitTest;
- подготовка данных - Fixture, Mocker, Stub, Spy;
- запуск тестов - TestRunner, TestSuite.
- Определяем утилиты и помоники:
- вспомогательный функционал - Helper, Utils, Extensions, Wrapper;
- конфигурация - Config, Configurator, Settings;
- инициализация - Bootstrapper, Initializer.
Разумеется, может быть различный набор комбинаций всех этих классов, так что такое разделение весьма условно. Сначала потребуется нарисовать схему, расставить логические элементы (узлы), которые важно будет рассмотреть с точки зрения необходимости декомпозиции. Начиная проектировать, сначала нужно все продумать на начальных верхних уровнях, потом спускаться по иерархии, разделяя по надобности.
Когда будут понятны этапы, компоненты уже создаются по формуле:
<A>+<B>+<C>
где:
- A - контекст/объект - что это, о ком или о чём класс (User, Book, Order, Payment);
- B - роль/назначение - зачем нужен, какую задачу выполняет (Service, Repository, Validator, Sender);
- C - тип/паттерн - какой тип класса или паттерн (Impl, Adapter, Helper, Handler).
Примеры:
- UserService - контекст User, роль Service;
- PaymentValidator - контекст Payment, роль Validator;
- LoggerAdapter - контекст Logger, роль и тип - Adapter.
Конечно, длинных имён ради точности делать не стоит, но и не нужно сокращать до невнятности (UsrSrv будет не так понятно, как UserService). Чем конкретнее, тем лучше, и когда паттерны явно применяются, лучше прямо это указывать в названиях. Порядок элементов можно и менять, но очевидно ServiceUser воспринимается немного иначе.